#include <openssl/ssl.h>
#include <openssl/err.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <net/route.h>
#include "rpc_ssl.h"
#include "rpc_server.h"

void ssl_low_level_init()
{
	/* Init ssl library */
    	SSL_library_init();
	/* Load all algorithms */
	OpenSSL_add_all_algorithms();
	/* Load openssl wrong infor */
	SSL_load_error_strings();	
}

void rpc_ssl_ctx_init(SSL_CTX *ctx)
{
    	//SSL_CTX *ctx = rpc_ctx;

	/* You can use v2 & v3 compatible mode, you can also 
	use v2 | v3 mode like : SSLv2_server_method(), 
	SSLv3_server_method */
	ctx = SSL_CTX_new(SSLv23_server_method());
	if(ctx == NULL){
		ERR_print_errors_fp(stdout);
		exit(1);
	}

	/* Load user authentication, and used to send to client with public key */
	if(SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0){
		ERR_print_errors_fp(stdout);
		exit(1);
	}

	/* Load user private key */
	if(SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0){
		ERR_print_errors_fp(stdout);
		exit(1);
	}
	/* Check user private key is right ? s*/
	if (!SSL_CTX_check_private_key(ctx)) {
    		fprintf(stdout,"Private key does not match the certificate public key\n");
    		exit(1);
  	}

	printf("rpc ssl init success !\n");
}

int rpc_ssl_write(SSL *rpcSock, unsigned char * response,int responseLen)
{
	int wl,slice,resend = 0;
	if (responseLen < slice_size)
	{
		wl = SSL_write(rpcSock, response, responseLen);
		resend =0;
		while ((wl != responseLen) && (resend < RSEND_TIMES))
		{
			printf("resend : w1=%d,len =%d\r\n",wl,responseLen);
			taskDelay(2);
			if(wl < 0)
				wl =0;
			wl += SSL_write(rpcSock, &response[wl], responseLen-wl);
			resend++;
		}
		if(wl!=responseLen)
		{
			printf("send error : w1=%d,responseLen =%d\r\n",wl,responseLen);
		}
	}
	else
	{
		for(slice=0;slice<(int)(responseLen/slice_size);slice++)
		{
			wl = SSL_write(rpcSock,&response[slice*slice_size], slice_size);
			resend = 0;
			while ((wl != slice_size) && (resend < RSEND_TIMES))
			{
				printf("resend : wl=%d,len = 800\r\n",wl);
				taskDelay(2);
				if(wl < 0)
					wl =0;
				wl += SSL_write(rpcSock, &response[wl], slice_size-wl);
				resend++;
			}
			if(wl!=slice_size)
			{
				printf("send error : wl=%d,len =800\r\n",wl);
			}
			/*printf("cli SmisServSubmit write %d bytes\r\n",wl);*/
		}
		if ((responseLen-(slice*slice_size))>0)
		{
			wl = SSL_write(rpcSock,&response[slice*slice_size], responseLen-(slice*slice_size));
			resend = 0;
			while ((wl != (responseLen-(slice*slice_size))) && (resend < RSEND_TIMES))
			{
				printf("resend : wl=%d,len = %d\r\n",wl,responseLen-(slice*slice_size));
				taskDelay(2);
				if(wl < 0)
					wl =0;
				wl += SSL_write(rpcSock, &response[wl], responseLen-(slice*slice_size)-wl);
				resend++;
			}
			if(wl != (responseLen-(slice * slice_size)))
			{
				printf("send error : wl=%d,len = %d\r\n",wl,(responseLen-(slice*slice_size)));
			}
		}
	}
	return 0;
}

int rpc_ssl_read(SSL *ssl,unsigned char *request, int request_len)
{
	int nbytes, pkt_len, offset = 0;
    	unsigned char buf[RPC_READ_BUF];
    	int first_flag = 1;

	if(request_len > RPC_READ_BUF) return -1;
	
	while (1)
	{
		memset(buf, 0, RPC_READ_BUF);
		/* Read raw data from socket */
		nbytes = SSL_read(ssl, buf, RPC_READ_BUF - 1);
		if (nbytes <= 0) return -1;
		
		/* length field of packet is in the first packet */
		if (first_flag == 1)
		{
			pkt_len = cc_rpc_get_pkt_len(buf);
			if (pkt_len > RPC_READ_BUF-1)
			{
				printf("\r\n cc packet length is too long \r\n");
			}
			first_flag = 0;
		}
		memcpy(request + offset, buf, nbytes);
		if (nbytes+offset != pkt_len)
		{
			offset += nbytes;
			continue;
		}
		else break;
	}
	return pkt_len;
}

int ssl_server(int libIndex, unsigned short port,  int family)
{
	int accept_sock;
	unsigned char buff[20] ;
	unsigned char * str = "hello openssl server !\n";
	int rpc_sock;
	SSL_CTX *ctx;
	int ret, i;
  	union sockunion su;

	ssl_low_level_init();
	printf("ssl low leverl init okay !\n");
	/* You can use v2 & v3 compatible mode, you can also 
	use v2 | v3 mode like : SSLv2_server_method(), 
	SSLv3_server_method */
	ctx = SSL_CTX_new(SSLv23_server_method());
	if(ctx == NULL){
		ERR_print_errors_fp(stdout);
		printf("SSL_CTX_new\n");
		exit(1);
	}

	/* Load user authentication, and used to send to client with public key */
	if(SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0){
		ERR_print_errors_fp(stdout);
		printf("SSL_CTX_use_certificate_file\n");
		exit(1);
	}

	/* Load user private key */
	if(SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0){
		ERR_print_errors_fp(stdout);
		printf("SSL_CTX_use_PrivateKey_file\n");
		exit(1);
	}
	/* Check user private key is right ? s*/
	if (!SSL_CTX_check_private_key(ctx)) {
    		fprintf(stdout,"Private key does not match the certificate public key\n");
		printf("SSL_CTX_check_private_key\n");	
    		exit(1);
  	}
	printf("rpc ssl init success !\n");
	memset (&su, 0, sizeof (union sockunion));
	su.sa.sa_family = family;

	/* Prepare TCP socket for receiving connections */
	/* Make new socket. */
	accept_sock = rpc_sockunion_stream_socket (libIndex, &su);
	if (accept_sock < 0)
	{
		printf("rpc server create socket okay !\n");
		return (-1);
	}
	/* This is server, so reuse address. */
	rpc_sockopt_reuseaddr (libIndex, accept_sock);
	rpc_sockopt_reuseport (libIndex, accept_sock);
	/*sockopt_nodelay(accept_sock);*/

	/* Bind socket to universal address and given port. */
	ret = rpc_sockunion_bind (libIndex, accept_sock, &su, port, NULL);
	if (ret < 0)
	{
		close (accept_sock);	/* Avoid sd leak. */
		return (-1);
	}

	/* Listen socket under queue 3. */
	/*  ret = listen (accept_sock, 3);*/
	ret = listen (accept_sock, 0);
	if (ret < 0) 
	{
		/*zlog (libIndex, NULL, LOG_WARNING, "can't listen socket");*/
		close (accept_sock);	/* Avoid sd leak. */
		return (-1);
	}
	printf("\r\n RPC listen port: %d \r\n", port);
	
	while(1)
	{
		SSL *ssl;
		rpc_sock = rpc_sockunion_accept (libIndex, accept_sock, &su);
       	if (rpc_sock < 0)
       	{
        		/*zlog_warn (libIndex, "can't accept vty socket : %s", strerror (errno));*/
			printf("\r\n func = %s line = %d accept connect request failure !\r\n", __FUNCTION__, __LINE__);
        		return (-1);
       	}
		else{
		
			printf("RPC socket = %d\n", rpc_sock);
		}	

		 ssl = SSL_new(ctx);	
   		 SSL_set_fd(ssl, rpc_sock);

		if(SSL_accept (ssl) < 0)
		{
       		printf("SSL_accept error\n");
			return (-1);
   		}

		int len = rpc_ssl_write(ssl, str, sizeof(str));
		if(len < 0){
			printf("write error !\
				\n");
		}

		memset(buff, 0, 20);
		len = rpc_ssl_read(ssl, buff, 19);
		if(len > 0){
			for(i = 0; i < len; i++)
				printf("%02x ", buff[i]);
			printf("\n");
		}
	}
}
#if SSL_MAIN_TEST
int main()
{
	ssl_server(0, 3369, AF_INET);

	return 0;
}
#endif
